#include "OP_CreateRectangle.h"
#include "../Geometries/PolygonMesh.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

///<************************************************************************************************************>
///<************************************* region CONSTANTS DEFINITION ******************************************>
///<************************************************************************************************************>

/***************************************************************************************************************
************************************** endregion CONSTANTS DEFINITION ******************************************
****************************************************************************************************************/












/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OP_CreateRectangle::OP_CreateRectangle (vector<CIView::View*>& v)
: Operation (v), rectangle (0), rectangleDetail (0), height (0.002), width (0.002) {

    newSelectionMode = scene.GetSelectionMode();
}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::OP_CreateRectangle::~OP_CreateRectangle() {
    if (status != COMPLETED)
        delete rectangle;
}










///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to execute directly the operation 
/// </summary>
/// <returns> The operation status </returns>
int CIOps::OP_CreateRectangle::Exec() {
    switch (status) {
        case INITIAL:
            status = WAITING_FOR_EVENTS;
            break;
    }
    return status;        
}

/// <summary> 
/// Method called to undo the operation 
/// </summary>
void CIOps::OP_CreateRectangle::Undo() {
    status = UNDO;      

    scene.RemoveGeometry (rectangle->GetName().c_str());   //Removes the geometry from the 3D scene
    RestoreOldSelection();                              //Restores the old selection status
    UpdateViews();                                      //Updates the 3D views
}

/// <summary> 
/// Method called to redo the operation 
/// </summary>
void CIOps::OP_CreateRectangle::Redo() {
    status = COMPLETED;    

    scene.AddGeometry (rectangle);                     //Re-adds the geometry in the 3D scene  
    RestoreNewSelection();                          //Restore the new selectio status
    UpdateViews();                                  //Updates the 3D views
}

/// <summary> 
/// Mouse Pressed event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse press event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Pressed event </param>
void CIOps::OP_CreateRectangle::MousePressEvent (CIView::View* view, QMouseEvent* e) {
    float x = e->x();
    float y = e->y();
    
    SaveOldSelection();                             //Saves the old selection status
    scene.DeselectAllFaces();                       //Deselects all faces       //TODO -> Deselect all (included geometries and groups)

    view->WindowToWorldCoordinates (&x, &y);        //Unproject the mouse cursor coordinates

    Orientation& cameraOrient = view->GetCameraOrientation();

    //Computing the mouse cursor coordinates (in world coordinates)
    switch (view->GetCameraType()) {
        case Camera::TOP:
            topLeft.x = x;
            topLeft.z = y;
            rotation.x = 90.0f;
            break;
        
        case Camera::FRONT:
            topLeft.x = x;
            topLeft.y = -y;
            break;
        
        case Camera::RIGHT:          
            topLeft.z = -x;  
            topLeft.y = -y;
            rotation.y = -90.0f;
            break;
    }

    unsigned int id = scene.GenerateNewGeometryID();        //Gets a reference to the number of geometries that have been added to the scene
    stringstream name;
    name << "Rectangle" << id;                              //Sets the name of the new triangle mesh 

    rectangle = new PolygonMesh (name.str());                       //Creates the rectangle 

    //Creating the rectangle with triangles as faces
    //Adding the 4 vertices
    VertexLib& rectangleVertices = rectangle->GetVertices();
    for (int i=0; i<4; i++)
        rectangleVertices.push_back (new Vertex (topLeft));

    //Placing vertices
    rectangleVertices[1]->x += width;
    rectangleVertices[2]->y += height;
    rectangleVertices[3]->x += width;
    rectangleVertices[3]->y += height;

    //Creating the rectangle faces (2 triangles) counterclockwise winding    
    FaceLib& faces = rectangle->GetFaces();
    AddFace (rectangleVertices[1], rectangleVertices[0], rectangleVertices[2], faces);
    AddFace (rectangleVertices[1], rectangleVertices[2], rectangleVertices[3], faces);

    //Applying the rotation computed according to the current camera orientation
    Matrix m, m1, m2, m3;
    m1.SetTranslation (-topLeft.x, -topLeft.y, -topLeft.z);
    m2.SetRotation (rotation.x, rotation.y, rotation.z);
    m3.SetTranslation (topLeft.x, topLeft.y, topLeft.z);
    m = m1 * m2 * m3;
    rectangle->ApplyMatrix (m);

    scene.AddGeometry (rectangle);                                  //Adds the rectangle in the 3D scene

    UpdateViews();                                                  //Updates the 3D views
}

/// <summary> 
/// Mouse Released event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse release event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Released event </param>
void CIOps::OP_CreateRectangle::MouseReleaseEvent (CIView::View* view, QMouseEvent* e) {
    status = COMPLETED;

    view->SetPrimitivesSelected (true);                     //Signals that there is a primitive selected

    SaveNewSelection();                     //Saves the new selection status
    rectangle->UpdateBoundingBox();         //Updates the rectangle bounding box
}

/// <summary> 
/// Mouse Moved event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse move event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Moved event </param>
void CIOps::OP_CreateRectangle::MouseMoveEvent (CIView::View* view, QMouseEvent* e) {
    float x = e->x();
    float y = e->y();

    view->WindowToWorldCoordinates (&x, &y);            //Unproject the mouse cursor coordinates

    Orientation& cameraOrient = view->GetCameraOrientation();

    Vector3D drag;
    float newWidth, scaleWidth; 
    float newHeight, scaleHeight;
    Matrix scaling;

    //Computing the new width value according to the mouse drag values
    switch (view->GetCameraType()) {
        case Camera::TOP:
            drag.x = x - topLeft.x;
            drag.z = y - topLeft.z;
            newWidth = drag.x ? drag.x : width;
            newHeight = drag.z ? drag.z : height;

            scaleWidth = newWidth / width;
            scaleHeight = newHeight / height;

            scaling.SetScaling (scaleWidth, scaleWidth, scaleHeight);            
            break;
        
        case Camera::FRONT:
            drag.x = x - topLeft.x;
            drag.y = -topLeft.y - y;
            newWidth = drag.x ? drag.x : width;
            newHeight = drag.y ? drag.y : height;

            scaleWidth = newWidth / width;
            scaleHeight = newHeight / height;

            scaling.SetScaling (scaleWidth, scaleHeight, scaleWidth);         
            break;
        
        case Camera::RIGHT:         
            drag.z = -topLeft.z - x;
            drag.y = -topLeft.y - y;            
            newWidth = drag.z ? drag.z : width;
            newHeight = drag.y ? drag.y : height;

            scaleWidth = newWidth / width;
            scaleHeight = newHeight / height;

            scaling.SetScaling (scaleWidth, scaleHeight, scaleWidth);         
            break;
    }    

    height = newHeight;
    width = newWidth;

    //Scaling the rectangle according to the new width and height
    Matrix m1, m2;
    m1.SetTranslation (-topLeft.x, -topLeft.y, -topLeft.z);
    m2.SetTranslation (topLeft.x, topLeft.y, topLeft.z);
    
    Matrix m;
    m = m1*scaling*m2;
    rectangle->ApplyMatrix (m);

    UpdateViews();                                      //Updates the 3D views
}

/// <summary> 
/// Wheel event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse wheel event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Wheel event </param>
void CIOps::OP_CreateRectangle::WheelEvent (CIView::View* view, QWheelEvent* e) {
    if ((e->delta() > 0 && rectangleDetail == 4) || (e->delta() <= 0 && rectangleDetail == 0))
        return;

    VertexLib newVertices;  
    FaceLib newFaces;
    FaceLib& faces = rectangle->GetFaces();

    if (e->delta() > 0) {
        rectangleDetail++;              //Increases the rectangle triangle tessellation detail

        FaceIter f = faces.begin();

        Vertex* v1;
        Vertex* v2;
        Vertex* v3;
        Vertex* m1;
        Vertex* m2;
        Vertex* m3;
        
        map<Vertex*, map<Vertex*,Vertex*> > midpoint;
        map<Vertex*,Vertex*> clone;

        //We must create for each face (triangle) four new triangles, 
        //formed connecting the vertices of the initial face, with
        //the midpoints of the three edges of the face
        while (f != faces.end()) {
            const VertexLib& oldVertices = (*f)->vertices;

            if (oldVertices.size() == 3) { 
                v1 = clone[oldVertices[0]];
                if (!v1) {
                    v1 = clone[oldVertices[0]] = new Vertex (*oldVertices[0]);
                    newVertices.push_back (v1);
                }
                
                v2 = clone[oldVertices[1]];
                if (!v2) {
                    v2 = clone[oldVertices[1]] = new Vertex (*oldVertices[1]);
                    newVertices.push_back (v2);
                }
                
                v3 = clone[oldVertices[2]];
                if (!v3) {
                    v3 = clone[oldVertices[2]] = new Vertex (*oldVertices[2]);
                    newVertices.push_back (v3);
                }

                //Computing mid points of the three edges
                m1 = midpoint[v1][v2];
                if (!m1) {
                    m1 = midpoint[v1][v2] = Vertex::MidPoint (v1, v2);
                    midpoint[v2][v1] = m1;
                    newVertices.push_back (m1);
                }
                
                m2 = midpoint[v2][v3];
                if (!m2) {
                    m2 = midpoint[v2][v3] = Vertex::MidPoint (v2, v3);
                    midpoint[v3][v2] = m2;
                    newVertices.push_back (m2);
                }
                
                m3 = midpoint[v3][v1];
                if (!m3) {
                    m3 = midpoint[v3][v1] = Vertex::MidPoint (v3, v1);
                    midpoint[v1][v3] = m3;
                    newVertices.push_back (m3);
                }

                //Creating the new faces
                AddFace (v1, m1, m3, newFaces);
                AddFace (v2, m2, m1, newFaces);
                AddFace (v3, m3, m2, newFaces);
                AddFace (m1, m2, m3, newFaces);
            }

            f++;
        }
    }
    else {
        //TODO
        rectangleDetail--;

        Vertex* v1;
        Vertex* v2;
        Vertex* v3;
        Vertex* clone1;
        Vertex* clone2;
        Vertex* clone3;
        
        map<Vertex*,Vertex*> clone;
        
        //We must apply the inverse reasoning than before
        //for each group of 4 faces (triangles), we must
        //build a triangle formed by the first vertex 
        //of the first three faces (the fourth is discarded
        //because it was created unifying the three midpoints)
        for (int i=0; i<faces.size(); i+=4) {
            v1 = faces[i]->vertices[0];
            v2 = faces[i+1]->vertices[0];
            v3 = faces[i+2]->vertices[0];

            clone1 = clone[v1];
            clone2 = clone[v2];
            clone3 = clone[v3];

            if (!clone1) {
                clone1 = clone[v1] = new Vertex (*v1);
                newVertices.push_back (clone1);
            }

            if (!clone2) {
                clone2 = clone[v2] = new Vertex (*v2);
                newVertices.push_back (clone2);
            }

            if (!clone3) {
                clone3 = clone[v3] = new Vertex (*v3);  
                newVertices.push_back (clone3);          
            }

            //Creating the new face
            AddFace (clone1, clone2, clone3, newFaces);            
        }        
    }

    rectangle->DeleteAllVertices();                        //Deletes the old rectangle vertices and faces

    rectangle->SetVertices(newVertices);
    rectangle->SetFaces(newFaces);
    rectangle->ComputeNormals();

    UpdateViews();                                      //Updates the 3D views
}


/// <summary> 
/// Method called to add a face (triangle) to the rectangle
/// </summary>
/// <param name="v1"> The first vertex </param>
/// <param name="v2"> The second vertex </param>
/// <param name="v3"> The third vertex </param>
/// <param name="newFaces"> The faces library into which insert the new face </param>
void CIOps::OP_CreateRectangle::AddFace (Vertex *v1, Vertex *v2, Vertex *v3, FaceLib& newFaces) {
    VertexLib newFaceVertices(3);
    Face* newFace;

    newFaceVertices[0] = v1;
    newFaceVertices[1] = v2;
    newFaceVertices[2] = v3;

    newFace = new Face (newFaceVertices);
    newFace->ComputeNormal();

    newFaces.push_back (newFace);
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/









///<***********************************************************************************************************>
///<*********************************** region Triangles Tessellation *****************************************>
///<***********************************************************************************************************>

/***************************************************************************************************************
*********************************** endregion Triangles Tessellation *******************************************
****************************************************************************************************************/
